/**
 *
 * @file tsar.c
 *
 * @brief This file contains the whole core system
 *
 * $Id: tsar.c 84 2009-01-09 16:02:36Z henri.doreau $
 */

/*
 * This file is part of Tsar.
 *
 * Tsar is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tsar is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tsar.  If not, see <http://www.gnu.org/licenses/>.
 */




#include <tsar.h>       /* Tsar core */
#include <sniffer.h>
#include <webserver.h>



/* -- Exit as soon as possible when this flag turns to false */
bool_t Loop = true;
/* --- */


int tsar_start_session( opt_t * o )
{
    int retVal = 0;
    tsar_t session;

    /* Start both sniffer and webserver services */
    if ( tsar_startup( &session, o ) )
    {
        print_error( "An error occured when starting Tsar session\n" );
        retVal = 1;
    }
    /* sockpool interface initialization */
    else if ( ( session.pool = sockpool_init() ) == NULL )
    {
        print_error( "Sockpool initialization failed!\n" );
        retVal = 2;
    }
    /* attach callbacks to events using sockpool "exported" functions */
    else if ( tsar_set_callbacks( &session ) )
    {
        print_error( "Error registering events\n" );
        retVal = 3;
    }
    /* Success : start main loop */
    else if ( sockpool_loop( session.pool ) )
    {
        /* 
         * Exitted on signal receipt or error
         * but returns not null on error
         */
        retVal = 4;
    }
    /* Try to clase whatever needs to be closed */
    tsar_shutdown( &session );

    return retVal;
}

int tsar_startup( tsar_t * session, opt_t * o )
{
    int retVal;
    unsigned int i;

    /* Print out a smart starting message */
    print_starting_message( o->iface );
    set_signal_handler();
    /* Set future handles to NULL */
    session->pool    = NULL;
    session->sniffer = NULL;
    session->dumpFilename = o->pcapFile;
    /* Future sockets to deal with clients requests, init at -1 (invalid) */
    for ( i = 0; i < MAX_CONNECTIONS; i++ )
        session->webserverClients[i] = -1;

    if ( o->startSniffer )
    {
        if ( ( session->sniffer = start_sniffer( o ) ) == NULL )
            return 1;   /* XXX : rewrite this cleaner */
    }
    if ( start_webserver( session, o ) )
    {
        retVal = 2;
    }
    else
    {
        debug( "Tsar startup accomplished successfully!\n" );
        retVal = 0;
    }

    return retVal;
}

void tsar_shutdown( tsar_t * session )
{
    if ( session->sniffer )
    {
        print( MESSAGE_NOTICE, "Shutdown network sniffer...\n" );
        shutdown_sniffer( session->sniffer );
    }

    print( MESSAGE_NOTICE, "Shutdown http server...\n" );
    shutdown_webserver( session );

    print( MESSAGE_NOTICE, "Shutdown sockpool events manager...\n" );
    sockpool_shutdown( session->pool );

    /* FIXME : output API is initialized at
     * option parsing time and shutdown now... */
    print( MESSAGE_NOTICE, "Shutdown output module : Bye!\n" );
    end_output();   /* logging nd printing shutdown */
}

void print_starting_message( char * interface )
{
    time_t now;

    time( &now );
    print( MESSAGE_ALERT, "\n\t+] Starting Tsar %s ( %s ) on interface %s at %s\n", 
            _VERSION_,          /* Tsar version */
            _URL_,              /* Tsar homepage */
            interface,          /* Network interface Tsar is bound to */
            ctime( &now ) );    /* Starting date */
}

void set_signal_handler( void )
{
    struct sigaction sigHandler;

    /* the callback function returns  */
    sigHandler.sa_handler = stop_loop;
    /* No flags needed */
    sigHandler.sa_flags   = 0x00;
    
    /* We'll catch as much signals as possible */
    if ( sigfillset( &sigHandler.sa_mask ) )
    {
        die( "sigfillset" );
    }
    /* Set signal handling for interrupt signals */
    else if ( sigaction( SIGINT, &sigHandler, 0 ) )
    {
        die( "sigaction" );
    }
    else
    {
        /* Success case */
        debug( "Signal handler set\n" );
    }
}

void stop_loop( int signum )
{
    print( MESSAGE_CRITICAL, "\nProgram received signal (%d)\n", signum );
    Loop = false;
}

int tsar_set_callbacks( tsar_t * session )
{
    /* User can choose not to launch the sniffer */
    if ( session->sniffer )
    {
        debug( "Registering event for pcap fd\n" );
        /* Sniffer callback : store every packet in the dumpfile */
        sockpool_add_event( 
                session->pool,  /* Our previously initialized events handler */
                EVENT_TYPE_READ,    /* Exec callback when datas are readable on our socket */
                &sniffer_dump,      /* Callback to execute */
                pcap_get_selectable_fd( session->sniffer->receiver ),   /* socket to watch */
                ( void * )session );                                    /* Pass this pointer to the callback */
    }

    debug( "Registering event for the built in http web server's listening socket\n" );
    /* This socket is listening for incoming TCP connections attemps, then new sockets will be registered to process HTTP exchange */
    sockpool_add_event(
            session->pool,          /* Our previously initialized events handler */
            EVENT_TYPE_READ,        /* Exec callback when datas are readable on our socket */
            &webserver_accept,      /* Callback to execute */
            session->webserverSock, /* socket to watch */
            ( void * )session );    /* Pass this pointer to the callback */

    return 0;
}

